#include <cstdio>
#include <cmath>
#include "complex.h"

Complex::Complex(double x, double y)
    : re(x), im(y)
{
}

Complex &Complex::operator+=(const Complex &complex)
{
    re += complex.re;
    im += complex.im;
    return *this;
}

Complex Complex::operator+(const Complex &complex) const
{
    Complex result = *this;
    result += complex;
    return result;
}

Complex &Complex::operator-=(const Complex &complex)
{
    re -= complex.re;
    im -= complex.im;
    return *this;
}

Complex Complex::operator-(const Complex &complex) const
{
    Complex result = *this;
    result -= complex;
    return result;
}

Complex &Complex::operator*=(const Complex &complex)
{
    double temp = re*complex.re - im*complex.im;
    im = re*complex.im + im*complex.re;
    re = temp;
    return *this;
}

Complex Complex::operator*(const Complex &complex) const
{
    Complex result = *this;
    result *= complex;
    return result;
}

Complex &Complex::operator/=(const Complex &complex)
{
    double q = complex.im / complex.re;
    double d = complex.re + complex.im*q;
    re = (re + im*q)/d;
    im = (im - re*q)/d;
    return *this;
}

Complex Complex::operator/(const Complex &complex) const
{
    Complex result = *this;
    result /= complex;
    return result;
}

bool Complex::operator==(const Complex &complex)
{
    return re == complex.re && im == complex.im;
}

Complex &Complex::pow(int exponent)
{
    while (--exponent)
        operator*=(*this);
    return *this;
}

Complex &Complex::sq()
{
    return operator*=(*this);
}

#define sign(x) ((x) >= 0 ? 1 : -1)

Complex &Complex::sqrt()
{
    double x;
    double y;
    double u = re;
    double v = im;

    if (u > 0) {
        x = std::sqrt((u + std::sqrt(u*u + v*v))/2);
        y = v / (2*x);
    }
    else if (u < 0) {
        y = sign(v) * std::sqrt((-u + std::sqrt(u*u + v*v))/2);
        x = v / (2*y);
    }
    else {
        x = std::sqrt(fabs(v)/2);
        if (x > 0)
            y = v / (2*x);
        else
            y = 0;
    }

    re = x;
    im = y;
    return *this;
}

double Complex::real() const
{
    return re;
}
double Complex::imag() const
{
    return im;
}

double Complex::abs() const
{
    return std::sqrt(re*re + im*im);
}

double Complex::arg() const
{
    return atan2(im, re);
}

#define DEG2RAD(x) ((float) (M_PI/180.0)*(x))
#define RAD2DEG(x) ((float) (180.0/M_PI)*(x))

void Complex::print() const
{
    double phi = RAD2DEG( arg() );
    std::printf("%g + %gi = %g(cos(%g) + sin(%g))i\n",
           re, im, abs(), phi, phi);
}

Complex operator^(const Complex &complex, int exponent)
{
    Complex result = complex;
    result.pow(exponent);
    return result;
}
